"""
数据接口视图函数和视图类
FBV - Function-Based View - 基于函数的视图
CBV - Class-Based View - 基于类的视图
定制REST数据接口的三种方式:
1. 使用@api_view装饰器装饰视图函数
2. 继承APIView及其子类
3. 继承ModelViewSet并配置路由
"""
from uuid import uuid1

from django.db import transaction
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from drf_haystack.viewsets import HaystackViewSet
from rest_framework.decorators import api_view, throttle_classes, authentication_classes, permission_classes
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
from rest_framework_extensions.cache.mixins import CacheResponseMixin

from api.filters import EstateFilter, HouseInfoFilter
from api.helpers import CustomPagination, CustomAuthentication, CustomPermission
from api.serializers import DistrictSerializer, DistrictDetailSerializer, EstateSerializer, HouseTypeSerializer, \
    HouseInfoSerializer, HouseInfoIndexSerializer
from common.models import District, Estate, HouseType, HouseInfo, User, UserToken, LoginLog
from common.utils import to_md5_hex, get_ip_address


@api_view(['GET'])
@cache_page(timeout=None, cache='api')
@throttle_classes(())
def provinces(request):
    # 通过ORM框架从数据库中获取数据（资源）
    queryset = District.objects.filter(parent__isnull=True).only('distid', 'name')
    # 通过自定义的序列化器序列化数据
    serilizer = DistrictSerializer(queryset, many=True)
    # 通过序列化器获得序列化之后的数据（字典或列表）生成响应
    return Response(serilizer.data)


@api_view(['GET'])
@cache_page(timeout=300, cache='api')
# @authentication_classes((CustomAuthentication, ))
# @permission_classes((CustomPermission, ))
def cities(request, distid):
    district = District.objects.filter(distid=distid).first()
    serializer = DistrictDetailSerializer(district)
    return Response(serializer.data)


class EstateViewSet(ModelViewSet):
    queryset = Estate.objects.all()\
        .select_related('district').prefetch_related('agents')
    serializer_class = EstateSerializer
    pagination_class = CustomPagination
    # filter_backends = (DjangoFilterBackend, OrderingFilter)
    # filter_fields = ('district', 'name')
    filterset_class = EstateFilter
    ordering_fields = ('hot', )
    ordering = ('-hot', 'estateid')
    # authentication_classes = (CustomAuthentication, )
    # permission_classes = (CustomPermission, )


class HouseTypeViewSet(CacheResponseMixin, ModelViewSet):
    queryset = HouseType.objects.all()
    serializer_class = HouseTypeSerializer
    pagination_class = None
    # authentication_classes = (CustomAuthentication, )
    # permission_classes = (CustomPermission, )


@method_decorator(cache_page(timeout=120, cache='api'), name='list')
@method_decorator(cache_page(timeout=240, cache='api'), name='retrieve')
class HouseInfoViewSet(ReadOnlyModelViewSet):
    queryset = HouseInfo.objects.all()\
        .select_related('district')\
        .select_related('type')\
        .select_related('estate')\
        .select_related('agent')\
        .prefetch_related('tags')
    serializer_class = HouseInfoSerializer
    pagination_class = CustomPagination
    filterset_class = HouseInfoFilter
    ordering_fields = ('pubdate', 'estate__hot', 'price')
    ordering = ('-pubdate', )
    # authentication_classes = (CustomAuthentication, )


# pymysql - connection - autocommit(False)
# 提交事务 - commit() / 回滚事务 - rollback()
# 事务的ACID特性？
# Atomicity - 原子性 - 要么全做要么全不做
# Consistency - 一致性 - 事务前后数据状态要一致
# Isolation - 隔离性 - 并发事务不能看到彼此的中间状态
# Duration - 持久性 - 事务完成后数据要持久化
# 事务隔离级别 - 如果有并发事务访问数据，那么可能产生五种问题：
# 第1类丢失更新、第2类丢失更新、脏读、不可重复读、幻读
# 数据库底层通过锁来保护数据（行级锁、表级锁）
# MySQL底层的存储引擎：InnoDB / MyISAM
# 写SQL的时候不用每次都自己指定使用哪种锁来保护数据
# 数据库会根据我们设置的事务隔离级别自动选择对应的锁
# MySQL如何查看和设置事务（隔离级别）？
# 查看事务隔离级别：select @@tx_isolation;
# read uncommitted / read committed / repeatable read / serializable
# 从左向右，事务隔离级别和数据的安全性从低到高，并发性从高到低
# 全局设置：set global transaction isolation level read committed;
# 当前会话：set session transaction isolation level read committed;
@api_view(['POST'])
def login(request):
    resp_dict = {'code': 30000, 'message': '用户登录成功'}
    username = request.POST['username']
    password = request.POST['password']
    password = to_md5_hex(password)
    user = User.objects.filter(username=username, password=password)\
        .only('userid').prefetch_related('roles').first()
    if user:
        resp_dict['userid'] = request.session['userid'] = user.userid
        with transaction.atomic():
            resp_dict['token'] = token = uuid1().hex
            UserToken.objects.update_or_create(user=user, defaults={'token': token})
            current_time = timezone.now()
            delta = current_time - user.lastvisit
            if delta.days >= 1:
                user.point += 5
                user.lastvisit = current_time
                user.save()
            log = LoginLog()
            log.user = user
            log.ipaddr = get_ip_address(request)
            log.save()
    else:
        resp_dict['code'] = '30001'
        resp_dict['message'] = '用户名或密码错误'
    return Response(resp_dict)


class SearchViewSet(HaystackViewSet):
    index_models = [HouseInfo]
    serializer_class = HouseInfoIndexSerializer
